package com.example.judge.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.api.domain.UserExeResult;
import com.example.api.domain.dto.JudgeSubmitDTO;
import com.example.api.domain.vo.UserQuestionResultVO;
import com.example.common.core.constants.Constants;
import com.example.common.core.constants.JudgeConstants;
import com.example.common.core.enums.CodeRunStatus;
import com.example.judge.domain.SandBoxExecuteResult;
import com.example.judge.domain.UserSubmit;
import com.example.judge.mapper.UserSubmitMapper;
import com.example.judge.service.IJudgeService;
import com.example.judge.service.ISandboxPoolService;
import com.example.judge.service.ISandboxService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class JudgeServiceImpl implements IJudgeService {

    @Autowired
    private ISandboxService sandboxService;

    @Autowired
    private ISandboxPoolService sandboxPoolService;

    @Autowired
    private UserSubmitMapper userSubmitMapper;


    //这里只对执行结果进行判断,运行代码的方法单开一个service
    @Override
    public UserQuestionResultVO doJudgeJavaCode(JudgeSubmitDTO judgeSubmitDTO) {
        //将参数放到代码沙箱中执行
        //将执行代码和入参传进去
        SandBoxExecuteResult sandBoxExecuteResult =
                sandboxPoolService.exeJavaCode(judgeSubmitDTO.getUserId(),judgeSubmitDTO.getUserCode(),judgeSubmitDTO.getInputList());
        //对结果进行判断
        UserQuestionResultVO userQuestionResultVO = new UserQuestionResultVO();
        //有返回值 + 运行成功
        if (sandBoxExecuteResult != null && CodeRunStatus.SUCCEED.equals(sandBoxExecuteResult.getRunStatus())) {
            //结果比对,生成响应值
            userQuestionResultVO = doJudge(judgeSubmitDTO,sandBoxExecuteResult,userQuestionResultVO);
        } else {
            userQuestionResultVO.setScore(Constants.FALSE);
            if(sandBoxExecuteResult != null) {
                //从沙箱中获取错误信息
                userQuestionResultVO.setExeMessage(sandBoxExecuteResult.getExeMessage());
            } else {
                //报出未知异常
                userQuestionResultVO.setExeMessage(CodeRunStatus.UNKNOWN_FAILED.getMsg());
            }
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
        }
        //维护数据库数据
        saveUserSubmit(judgeSubmitDTO, userQuestionResultVO);

        return userQuestionResultVO;
    }

    //运行成功 + 有返回值进行返回值结果比对
    private UserQuestionResultVO doJudge(JudgeSubmitDTO judgeSubmitDTO,SandBoxExecuteResult sandBoxExecuteResult,UserQuestionResultVO userQuestionResultVO) {
        //比对执行结果,时间限制,空间限制
        List<String> exeOutputList = sandBoxExecuteResult.getOutputList();
        List<String> outputList = judgeSubmitDTO.getOutputList();
        //返回结果长度不一致
        if (outputList.size() != exeOutputList.size()) {
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMsg());
            return userQuestionResultVO;
        }

        //把输入,预期输出和实际输出整合一下给前端
        List<UserExeResult> userExeResultList = new ArrayList<>();
        //输出比对
        boolean passed = resultCompare(judgeSubmitDTO, exeOutputList, outputList, userExeResultList);
        //封装resultVO(passed,时间限制,空间限制)
        return assembleUserQuestionResultVO(judgeSubmitDTO, sandBoxExecuteResult, userQuestionResultVO, userExeResultList, passed);
    }

    //预期输出和实际输出比较
    private boolean resultCompare(JudgeSubmitDTO judgeSubmitDTO, List<String> exeOutputList,
                                  List<String> outputList, List<UserExeResult> userExeResultList) {
        //标识
        boolean passed = true;
        for (int index = 0; index < outputList.size(); index++) {
            String output = outputList.get(index);
            String exeOutPut = exeOutputList.get(index);
            String input = judgeSubmitDTO.getInputList().get(index);

            UserExeResult userExeResult = new UserExeResult();
            userExeResult.setInput(input);
            userExeResult.setOutput(output);
            userExeResult.setExeOutput(exeOutPut);

            userExeResultList.add(userExeResult);
            if (!output.equals(exeOutPut)) {
                passed = false;
            }
        }
        return passed;
    }

    private UserQuestionResultVO assembleUserQuestionResultVO(JudgeSubmitDTO judgeSubmitDTO,
                                                              SandBoxExecuteResult sandBoxExecuteResult,
                                                              UserQuestionResultVO userQuestionResultVO,
                                                              List<UserExeResult> userExeResultList, boolean passed) {
        userQuestionResultVO.setUserExeResultList(userExeResultList);
        if (!passed) {
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMsg());
            return userQuestionResultVO;
        }
        //空间限制
        if (sandBoxExecuteResult.getUseMemory() > judgeSubmitDTO.getSpaceLimit()) {
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_MEMORY.getMsg());
            return userQuestionResultVO;
        }
        //时间限制
        if (sandBoxExecuteResult.getUseTime() > judgeSubmitDTO.getTimeLimit()) {
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_TIME.getMsg());
            return userQuestionResultVO;
        }
        //全部通过, 这块得分的算法为 难度(0,1,2) * 100
        userQuestionResultVO.setPass(Constants.TRUE);
        int score = judgeSubmitDTO.getDifficulty() * JudgeConstants.DEFAULT_SCORE;
        userQuestionResultVO.setScore(score);
        return userQuestionResultVO;
    }

    //维护数据
    private void saveUserSubmit(JudgeSubmitDTO judgeSubmitDTO, UserQuestionResultVO userQuestionResultVO) {
        //往数据库中存储一份
        UserSubmit userSubmit = new UserSubmit();
        BeanUtils.copyProperties(userQuestionResultVO,userSubmit);
        userSubmit.setUserId(judgeSubmitDTO.getUserId());
        userSubmit.setQuestionId(judgeSubmitDTO.getQuestionId());
        userSubmit.setExamId(judgeSubmitDTO.getExamId());
        userSubmit.setProgramType(judgeSubmitDTO.getProgramType());
        userSubmit.setUserCode(judgeSubmitDTO.getUserCode());
        userSubmit.setCaseJudgeRes(JSON.toJSONString(userQuestionResultVO.getUserExeResultList()));
        userSubmit.setCreateBy(judgeSubmitDTO.getUserId());
        //只存一份(最后一份的数据),得找到用户上一条提交记录id
        //通过userId,questionId和examId去定位提交记录
        userSubmitMapper.delete(new LambdaQueryWrapper<UserSubmit>()
                .eq(UserSubmit::getUserId,judgeSubmitDTO.getUserId())
                .eq(UserSubmit::getQuestionId,judgeSubmitDTO.getQuestionId())
                .isNull(judgeSubmitDTO.getExamId() == null ,UserSubmit::getExamId)
                .eq(judgeSubmitDTO.getExamId()!=null,UserSubmit::getExamId,judgeSubmitDTO.getExamId()));
        userSubmitMapper.insert(userSubmit);
    }
}
